/*
 * GeoVISTA Center (Penn State, Dept. of Geography)
 * Copyright (c), 2003-2011,  Jin Chen and Alan M. MacEachren, GeoVISTA Center, Penn State University
 * All Rights Researved.
 *
 * This library is developed and distributed for free usage,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * You can redistribute and/or modify it under the terms of the
 * Simplified BSD License
 *
 *
 *
 *
 *
 *
 *
 * The table serves only for configuring purpose. It means the table will hold only a small number of row and column.
 * It has following functions
        
 * @author: jin Chen
 * @date: Aug 13, 2003$
 * @version: 1.0
 */
package edu.psu.geovista.app.pcp.toolkits.table ;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import java.util.Arrays;

public class ConfigureTable extends JTable{
    //protected TableModel dataModel;
    /* the index of the column which contain key values. A key value serves as primary key for a record.
     As ConfigureTable is to deal with simple case, we assume that a primary key is made up of only one attribute*/
    protected int IdColumnIndex=0; //by default, the 1st column serve as ID column
     // 
    private int[] columnWeight=new int[0];

    public ConfigureTable() {
       this.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION );
    }
    /**************************************************
     *                set/get data
     ***************************************************/
    public void setModel(TableModel dataModel) {
        super.setModel(dataModel) ;
        /*if (dataModel!=null){
            dataModel= dataModel;
            int rc=dataModel.getRowCount() ;
        }*/
        reDefineColumnWeight();

    }
      public void setColumnValue(String columnId, Boolean select) {
        ConfigureTableModel model = (ConfigureTableModel) getModel();
        int selColIndex = model.getColumnIndex(columnId);
        int rowCount = model.getRowCount();
        for (int i = 0; i < rowCount; i++) {
            setValueAt(select, i, selColIndex);
        }
    }
    public void setData(Object[][] data, String[] columnNames) {
        ConfigureTableModel dataModel=new ConfigureTableModel(data,columnNames);
        setModel(dataModel);
    }
    public Object[] getColumnValues(String  colName){
        ConfigureTableModel model=(ConfigureTableModel) dataModel;
        int col=model.getColumnIndex(colName);
        return getColumnValues(col);

    }
    public Object[] getColumnValues(int col){
        if(col<0){
            return new Object[0];
        }
        TableModel model= dataModel;
        int rowCount=model.getRowCount() ;
        Object[] cols=new Object[rowCount];
        for (int i=0;i<rowCount;i++){
            cols[i]=model.getValueAt(i,col);
        }
        return cols;
    }

    public void setValueAt(Object aValue, int row, String columnName) {

            int col=((ConfigureTableModel)this.getModel()).getColumnIndex(columnName);
            this.setValueAt(aValue,row,col);
    }
    public Object getValueAt(int row,String columnName){
        int col=((ConfigureTableModel)this.getModel()).getColumnIndex(columnName);
        return this.getValueAt(row,col);
    }
    /**
     * The order of element in array reflect the order the elements are located in the column
     * @param columnName
     * @return
     */
    public Object[] getSortValueAtColumn(String columnName){
          int numRow=this.getRowCount() ;
          Object[] obs=new Object[numRow];
          for (int i=0;i<obs.length ;i++){
              obs[i]=this.getValueAt(i,columnName);
          }
          return obs;
    }
    public Object[] getRowData(int row){
        return ((ConfigureTableModel)this.getModel()).getRowData(row);
    }
    /**
     *
     * @param row
     * @return    the primary key value of a row
     */
    public Object getRowID(int row){
        Object[] rowData = getRowData(row);
        return rowData[this.getIDColumnIndex() ];
    }
    /**
     *
     * @param id
     * @return    
     */
    public int getIndexOfRowWithID(Object id){
        int idColumnIndex = getIDColumnIndex();
        Object[] ids = this.getColumnValues(idColumnIndex);
        for (int i=0;i<ids.length ;i++){
            if(id.equals(ids[i]) ){
                return i;
            }
        }
        return -1;
    }


    /**************************************************
     *                operate on rows
     ***************************************************/
    public void moveTop(int row) {
        ((DefaultTableModel)this.dataModel).moveRow(row,row,0);
        this.repaint();
    }
    public void moveTop(int[] rows) {
        Arrays.sort(rows);
        for (int i=0;i<rows.length ;i++){
            moveTop(rows[i]);
        }


    }
    public void moveBottom(int row) {
        int rowNumber=dataModel.getRowCount();
        ((DefaultTableModel)this.dataModel) .moveRow(row,row,rowNumber-1);
        this.repaint();
    }
    public void moveBottom(int[] rows) {
        Arrays.sort(rows);
        for (int i=rows.length -1;i>=0;i--){
            moveBottom(rows[i]);
        }
    }
    /**
     * Move a record up one row. The moving is actually a physical movement on dataModel
     * Assume the table hold a small data set, the movement won't have big performance problem
     *
     * @param row
     */
    public void moveRowUp(int row) {
         this.moveRow(row,true);
    }
    public void moveRowDown(int row) {
        this.moveRow(row,false);
    }
    /**
     * Move multiple rows up
     * @param rows  the index of the rows which is to move up
     */
    public void moveRowsUp(int[] rows) {
        Arrays.sort(rows);
        // 
        // 
        for (int i=0;i<rows.length ;i++){
            this.moveRowUp(rows[i]);
        }
    }
    public void moveRowsDown(int[] rows) {
        Arrays.sort(rows);
        //For down, move larger index first, then move smaller index
        for (int i=rows.length-1;i>=0 ;i--){
            this.moveRowDown(rows[i]);
        }

    }
    /**
     *
     * @param row
     * @param up
     */
    private void moveRow(int row,boolean up) {
        if(up&&row<=0) return;//unable to move up anymore
        if(!up&&(row<0||row>=this.getRowCount()-1 )){
            //this.getRowCount()-1 is last row, can't be moved down
            return;//unable to move down anymore
        }

        int delta;
        if(up)delta=-1;
        else delta=1;
        Object[] rowData=this.getRowData(row);
       this.removeRow(row);
       this.insertRow(row+delta,rowData);
       this.setRowSelectionInterval(row+delta,row+delta);
    }
    /**
     *
     * @param rowData   a row data. an element (a object) is a cell 
     */
    public void addRow(Object[] rowData) {
        int lastRow=dataModel.getRowCount() ;
        insertRow(lastRow,rowData);
    }
    public void insertRow(int row, Object[] rowData) {
        ((ConfigureTableModel)this.dataModel).insertRow(row,rowData);
    }
    public void removeRow(int row) {
         /// int rows=dataModel.getRowCount() ;
          ((ConfigureTableModel)this.dataModel).removeRow(row);
    }
    public void removeRows(int[] rows) {
        Arrays.sort(rows);
        for (int i=rows.length -1;i>=0;i--){
            removeRow(rows[i]);
        }

    }

     public void removeAllRow() {
        ((ConfigureTableModel)this.getModel()).removeAllRow() ;
    }
    public int getRowIndexByID(Object id) {
        int idColIndex = this.getIDColumnIndex();
        Object[] ids = this.getColumnValues(idColIndex);
        for (int i=0;i<ids.length ;i++){
            if(ids[i].equals(id) )
                return i;
        }
        return -1;//id not found in ID column
    }
    /**************************************************
     *                operate on columns
     ***************************************************/

    /**
     * See if a record already contained in the table
     * @param pkColumnName the name of the column which serve as primary key
     * @param value        the primary key value of given record
     * @return
     */
    public boolean isRecordContained(String pkColumnName,Object value){
        for (int i=0;i<this.getRowCount() ;i++){
            Object pk= this.getValueAt(i,pkColumnName);
            if(value.equals(pk) )
                return true;
        }
        return false;
    }
    /**
     *
     * @param value
     * @return  true if the table's ID column contain the given value
     */
    public boolean isIDContained(Object value){
        String idColName=this.getIDColumnName();
        return this.isRecordContained(idColName,value);
    }




    //return the name of the column that act as ID of a row.
    protected String getIDColumnName(){
        TableModel model = this.getModel();
        return model.getColumnName(getIDColumnIndex());
        //return this.columnHeaders[getIDColumnIndex()];
    }
    //return the name of the column that act as ID of a row.
    public int getIDColumnIndex(){
        return IdColumnIndex;
    }
    public void setIDColumnIndex(int index) {
        this.IdColumnIndex =index;

    }

    /**
     * To set weight of columns
     *  
     * @param weights  weight of each column width. 0 means does not matter
     *
     */
    public void setColumnsWidth(int[] weights) {
        double total=0;
        for (int i=0;i<weights.length ;i++){
            total=total+weights[i];
        }
        double[] ws=new double[weights.length ];
        for (int i=0;i<ws.length ;i++){
            ws[i]=weights[i]/total;
        }
        setColumnsWidth(ws);
        this.columnWeight =weights;

    }
    /**
     *
     * @param weights  Array contains the weight of each column width. They must be expressed  in term of  a column weight/total weight
     */
    protected void setColumnsWidth(double[] weights){
        int numOfColumn=getColumnCount();
        if(weights==null||weights.length !=numOfColumn){
                 new IllegalArgumentException(this.getClass().getName() +" is passed a null argument or the array length is not correct");
                 return;
        }
        TableColumnModel cm = getColumnModel();
        int totalwidth=cm.getTotalColumnWidth() ;
        for (int i=0;i<numOfColumn;i++){
            int w=(int)Math.round(totalwidth*weights[i]);
            if(w>0){
                TableColumn c1 = cm.getColumn(i); //.getColumn("Name");
                c1.setPreferredWidth(w);
                //c1.setMinWidth(w);
            }
            //c1.setMaxWidth(50);
        }

    }
    /**
     * // 
     */
    protected void reDefineColumnWeight(){
        int[] weight;
       if(this.columnWeight ==null ||columnWeight.length ==0) return;
       int columnCount = this.getModel().getColumnCount();
       if(columnCount!=columnWeight.length ) {
           //throw new IllegalStateException() ;
           //throw new UnsupportedOperationException()
           weight=this.getDefaultWeight(columnCount);
       }
       else{
           weight=columnWeight;
       }
       this.setColumnsWidth(weight);

    }
    private int[] getDefaultWeight(int size){
        int w[]=new int[size];
        w[0]=1;
        for (int i=1;i<w.length ;i++){
            w[i]=2;
        }
        return w;
    }

    /**************************************************
     *                selection
     ***************************************************/
    public void setSelectedRow(int rowIndex){
        this.setColumnSelectionInterval(0,this.getColumnCount()-1 );
        this.setRowSelectionInterval(rowIndex,rowIndex);

    }
    public void setSelectedRowWithID(Object id){
        int index = this.getRowIndexByID(id);
        if(index>=0){
            this.setSelectedRow(index);
        }
    }
    /**
     * select continuous rows
     * @param from
     * @param to
     */
    public void setSelectedRows(int from, int to){
        this.setColumnSelectionInterval(0,this.getColumnCount()-1 );
        this.setRowSelectionInterval(from ,to);
    }
    public void addSelectedRow(int rowIndex){
        addRowSelectionInterval(rowIndex,rowIndex);
        setColumnSelectionInterval(0,getColumnCount()-1 );

    }


    /**************************************************
     *                sort
     ***************************************************/
    public void sort(String colname) {
        ConfigureTableModel model=(ConfigureTableModel) this.dataModel;
        model.sort(colname);
        //this.repaint();
    }
    public  void sort(int colIndex) {
        ConfigureTableModel model=(ConfigureTableModel) this.dataModel;
        model.sort(colIndex);
        ///this.repaint();
    }


    public void selectRows(int i, int i1) {
    }
}
